package com.zhangxu.microservice.system.biz.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zhangxu.microservice.common.utils.AssertUtils;
import com.zhangxu.microservice.common.utils.ListUtils;
import com.zhangxu.microservice.system.biz.convert.ResourceConverter;
import com.zhangxu.microservice.system.biz.convert.RoleConverter;
import com.zhangxu.microservice.system.biz.convert.RoleResourceConverter;
import com.zhangxu.microservice.system.biz.dto.*;
import com.zhangxu.microservice.system.biz.service.ApplicationService;
import com.zhangxu.microservice.system.biz.service.RoleResourceService;
import com.zhangxu.microservice.system.dao.entity.*;
import com.zhangxu.microservice.system.dao.service.*;
import com.zhangxu.microservice.common.context.EmployeeContextHolder;
import com.zhangxu.microservice.common.exception.BizException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 角色资源权限服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RoleResourceServiceImpl implements RoleResourceService {

    private final RoleResourceDao roleResourceDao;
    private final RoleDao roleDao;
    private final ResourceDao resourceDao;
    private final EmployeeRoleDao employeeRoleDao;
    private final RoleResourceConverter roleResourceConverter;
    private final RoleConverter roleConverter;
    private final ResourceConverter resourceConverter;
    private final ApplicationService applicationService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void assignRoleResource(AssignRoleResourceDTO assignRoleResourceDTO) {
        Long roleId = assignRoleResourceDTO.getRoleId();
        List<String> resourceCodes = assignRoleResourceDTO.getResourceCodes();
        // 检查角色是否存在
        RoleDO role = roleDao.getById(roleId);
        if (role == null || role.getDeleteTime() != 0L) {
            throw new BizException("角色不存在");
        }
        ApplicationDTO applicationDTO = applicationService.getApplicationById(role.getAppId());
        AssertUtils.notNull(applicationDTO, "应用不存在");
        // 先删除角色现有的资源权限
        clearRoleResource(roleId);
        // 批量插入新的角色资源权限
        if (CollUtil.isNotEmpty(resourceCodes)) {
            List<RoleResourceDO> roleResourceList = resourceCodes.stream().map(resourceCode -> {
                RoleResourceDO roleResource = new RoleResourceDO();
                roleResource.setRoleId(roleId);
                roleResource.setAppId(applicationDTO.getId());
                roleResource.setResourceCode(resourceCode);
                roleResource.setDeleteTime(0L);
                return roleResource;
            }).collect(Collectors.toList());
            roleResourceDao.saveBatch(roleResourceList);
        }

        log.info("角色[{}]在应用[{}]中资源权限分配成功，资源数量: {}", roleId, applicationDTO.getCode(), resourceCodes.size());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void clearRoleResource(Long roleId) {
        List<RoleResourceDO> roleResourceList = roleResourceDao.listByRoleId(roleId);
        if (!CollectionUtils.isEmpty(roleResourceList)) {
            Long currentTime = System.currentTimeMillis();
            roleResourceList.forEach(roleResource -> roleResource.setDeleteTime(currentTime));
            roleResourceDao.updateBatchById(roleResourceList);
        }
        log.info("角色[{}]清空所有资源权限成功", roleId);
    }


    @Override
    public List<ResourceDTO> getRoleResources(Long roleId) {
        //先查询角色
        RoleDO roleDO = roleDao.getById(roleId);
        AssertUtils.notNull(roleDO, "角色不存在");
        ApplicationDTO application = applicationService.getApplicationById(roleDO.getAppId());
        AssertUtils.notNull(application, "应用不存在");
        // 查询角色资源关联
        List<RoleResourceDO> roleResourceList = roleResourceDao.listByRoleIdAndAppId(roleId, application.getId());
        if (CollectionUtils.isEmpty(roleResourceList)) {
            return Collections.emptyList();
        }

        // 查询资源信息
        List<String> resourceCodes = ListUtils.mapList(roleResourceList, RoleResourceDO::getResourceCode);
        List<ResourceDO> resourceDOList = resourceDao.listByAppIdAndResourceCodes(application.getId(), resourceCodes);
        return resourceDOList.stream().map(resourceConverter::toResourceDTO).collect(Collectors.toList());
    }

    @Override
    public List<String> getRolePermissions(Long roleId) {
        List<ResourceDTO> resources = getRoleResources(roleId);
        return resources.stream().map(ResourceDTO::getCode).filter(resourceCode -> resourceCode != null && !resourceCode.trim().isEmpty()).distinct().collect(Collectors.toList());
    }

    @Override
    public List<String> getEmployeePermissions(Long employeeId) {
        // 获取员工的所有角色
        List<EmployeeRoleDO> employeeRoleList = employeeRoleDao.getByEmployeeId(employeeId);
        if (CollectionUtils.isEmpty(employeeRoleList)) {
            return Collections.emptyList();
        }
        // 获取所有角色的权限
        List<Long> roleIds = employeeRoleList.stream().map(EmployeeRoleDO::getRoleId).toList();
        return roleIds.stream().flatMap(roleId -> getRolePermissions(roleId).stream()).distinct().collect(Collectors.toList());
    }

    @Override
    public List<ResourceDTO> getEmployeeResources(Long employeeId) {
        // 获取员工的所有角色
        LambdaQueryWrapper<EmployeeRoleDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(EmployeeRoleDO::getEmployeeId, employeeId).eq(EmployeeRoleDO::getDeleteTime, 0L);
        List<EmployeeRoleDO> employeeRoleList = employeeRoleDao.list(queryWrapper);

        if (CollectionUtils.isEmpty(employeeRoleList)) {
            return Collections.emptyList();
        }

        // 获取所有角色的资源
        List<Long> roleIds = employeeRoleList.stream().map(EmployeeRoleDO::getRoleId).toList();

        return roleIds.stream().flatMap(roleId -> getRoleResources(roleId).stream()).collect(Collectors.groupingBy(ResourceDTO::getId)).values().stream().map(list -> list.get(0)) // 去重
                .collect(Collectors.toList());
    }

    @Override
    public boolean hasRoleResource(Long roleId, String resourceCode) {
        LambdaQueryWrapper<RoleResourceDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RoleResourceDO::getRoleId, roleId).eq(RoleResourceDO::getResourceCode, resourceCode).eq(RoleResourceDO::getDeleteTime, 0L);
        return roleResourceDao.count(queryWrapper) > 0;
    }

    @Override
    public boolean hasEmployeePermission(Long employeeId, String permission) {
        List<String> permissions = getEmployeePermissions(employeeId);
        return permissions.contains(permission);
    }

    @Override
    public List<RoleResourceDTO> getRoleResourceList(Long roleId) {
        LambdaQueryWrapper<RoleResourceDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(RoleResourceDO::getRoleId, roleId).eq(RoleResourceDO::getDeleteTime, 0L);
        List<RoleResourceDO> roleResourceList = roleResourceDao.list(queryWrapper);

        return roleResourceList.stream().map(roleResourceConverter::toRoleResourceDTO).collect(Collectors.toList());
    }

    @Override
    public boolean hasEmployeeResource(Long employeeId, String resourceCode) {
        // 获取员工的所有角色
        LambdaQueryWrapper<EmployeeRoleDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(EmployeeRoleDO::getEmployeeId, employeeId).eq(EmployeeRoleDO::getDeleteTime, 0L);
        List<EmployeeRoleDO> employeeRoleList = employeeRoleDao.list(queryWrapper);

        if (CollectionUtils.isEmpty(employeeRoleList)) {
            return false;
        }

        // 获取角色ID列表
        List<Long> roleIds = employeeRoleList.stream().map(EmployeeRoleDO::getRoleId).collect(Collectors.toList());

        // 查询角色是否有该资源权限
        LambdaQueryWrapper<RoleResourceDO> roleResourceQueryWrapper = new LambdaQueryWrapper<>();
        roleResourceQueryWrapper.in(RoleResourceDO::getRoleId, roleIds).eq(RoleResourceDO::getResourceCode, resourceCode).eq(RoleResourceDO::getDeleteTime, 0L);

        return roleResourceDao.count(roleResourceQueryWrapper) > 0;
    }
}